home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
SVGALINE.ZIP
/
vesasvga.txt
Wrap
Internet Message Format
|
1995-02-12
|
9KB
From: cs94169@assn013.cs.ualberta.ca (David Bond)
Newsgroups: rec.games.programmer
Subject: VESA SVGA - line code and info
Date: 6 Feb 1995 18:39:08 GMT
Hello everyone!
This is a mini-tutorial, and code, relating to VESA SVGA programming. The
code includes a line procedure which is based upon Bresenham's algorithm. It
is not blazingly fast, but hopefully it'll work on all SVGA cards with VESA
support, and it is pretty compact - no special cases for slopes.
Many people try to begin programming in SVGA modes straight from mode 13h, or
Xmode variants. They quickly encounter the problem of only 64K of vid mem
being accessable - falls a little short of the 300K required for 640x480x8bit!
The special address space A000h - AFFFh must be mapped to different parts of
the video memory to make use of it. This can be done via VESA functions. If
you don't yet have 'vesasp12.txt' (PCGPE contains this document) then I
suggest you get it from x2ftp.oulu.fi /pub/msdos/programming/specs/vesasp12.
This document details the VESA BIOS extensions used to get info on video
modes, set video modes, pan across a larger virtual screen, and set the CPU
window (A000-AFFF) to map to different places in video mem.
Even though VESA provides a common interface for SVGA cards, there are still
some specifics that have to be dealt with. The 'granularity' of the window
is the smallest amount by which it can be moved. A 64KB granularity with
1MB video memory means the CPU window can be mapped to one of 16 'chunks' in
this memory. A 4KB granularity has more potential mappings - the window is
still 64KB in size, but it can be positioned on any 4K boundary in video. I
know granularitys of 4K, 16K, 32K, and 64K exist. Some cards are switchable
(actually the only chipset I'm familiar with that has this option is Cirrus
Logic - defaults to 4K, can be set to 16K. I think this is necessary for
accessing >1MB).
I see two ways to manage this discrepancy. Code can assume 64K granularity
always, and the 'bank-switching' routines make sure the window is moved by this
ammount (4K gran would require inc/dec by 16). The other way is to deal with
each granularity differently - this is how the line code provided below
operates.
Finer granularity can speed up rendering. Line drawing will be used to
illustrate. The linear start address is calculated. The low 16 bits of this
address are mappable to the 64K window. The high order bits can be used to
locate the position of the window. With a 64K granularity, the high word is
our window location, and the low word is the displacement into the window.
With 4K gran, The low 12 bits are the offset, and remaining high bits are the
window location. If a line begins near the end of a 64K aligned chunk (linear
position 123840, say), and continues down a short distance, It'll cross a 64K
boundary. With 64K gran, the window will have to be moved. Using a 4K
granularity, the initial offset into a window can be kept below 4096. So,
lines that aren't too long can always be kept within the starting window.
Another advantage that fine granularity provides is easier alignment with the
edge of the screen. With a horizontal resolution of 640, 32 lines takes up
20KB, which is divisible by 4KB. If all windowing is then limited to be
aligned on these 20KB bounds, one will never have to worry about overflowing
past the end of the window while drawing across a scan-line. The windows
are positioned so that the 'bottom' of the window is on these 20K bounds.
Inner rendering loops that move across a horizontal line don't bother with
checking for a 'page-cross'. The outer loop checks for overflow when it moves
down to the next scan-line. A 64K granularity doesn't align until 512
vertical lines (320KB), which means the inner loop must check for
page-crossings within the scan-line.
Note: one way to create easy alignment with the edge is to change the length
of a scanline to a power of 2 (say 1024). This wastes video memory, but
it can be well worth it. Check vesasp12.txt for setting this.
The line procedure, below, does take advantage of positioning the top of the
window as close to the top of the line as it can. Thus window moving for
mid-length lines is reduced for cards that have smaller granularities. It
does not take advantage of alignment with the screen edge. The code is made
to be fairly 'straight-forward', not much fancy is done - it's just simple,
flexible, small, and I hope easy to understand. One easy optimization to add
is to check if the endpoints lie in different window addresses - if not, a
routine without a page-cross check can be called; otherwise the standard
routine is called.
Careful eyes may notice that lines are always rendered from top to bottom, but
I have a macro to move the CPU window UP! A situation where this is needed:
A window begins 382 pixels across on a scan-line. A line is started just two
pixels into the window (at 383). The endpoint is on the far left of the screen
(0), and 5 pixels down from start. The line is going to begin with a string of
pixels straight to the left - passing BACKWARD through the window boundary.
This occurance requires the 'PageUp' macro. If alignment is done with the
screen edge, this isn't necessary.
This code is provided for learning purposes, and may be used in any fashion
desired - it's free! If the code doesn't work for you, please let me know. I
haven't had opportunity to test it on other systems. It didn't get a rigorous
test on mine either - paging is untested. Conversion to other resolutions is
pretty simple. The linear address calculation is all that has to be modified
(I think!?) - 'bx' may be too small at higher resoultions - use 'ebx'.
This can be assembled with: tasm /m2 /ml <filename>
tlink /3 <filename>
Or pieces can be extracted, and interfaced to whatever you wish,
however you wish.
-Anthony Tavener 'Daoloth of MetaSentience'
-cs94169@cs.ualberta.ca (Temporary - friend's account)
---CODE BEGIN---
.486
code segment para public use16
assume cs:code
PgDown macro
push bx
push dx
xor bx,bx
mov dx,cs:winpos
add dx,cs:disp64k
mov cs:winpos,dx
call cs:winfunc
pop dx
pop bx
endm
PgUp macro
push bx
push dx
xor bx,bx
mov dx,cs:winpos
sub dx,1
mov cs:winpos,dx
call cs:winfunc
add di,cs:granmask
inc di
pop dx
pop bx
endm
mov ax,seg stk ;\
mov ss,ax ;.set up program stack
mov sp,200h ;/
call GetVESA ;init variables related to VESA support
mov ax,4f02h ;\
mov bx,0101h ;.VESA mode 101h (640x480x8bit)
int 10h ;/
mov ax,0a000h
mov ds,ax
mov eax,10h ;\
mov ebx,13h
mov ecx,20bh ;test Lin procedure
mov edx,1a1h
mov ebp,21h
call Lin ;/
mov ax,4c00h
int 21h
GetVESA proc
;This is just a hack to get the window-function address for a direct call,
;and to initialize variables based upon the window granularity.
mov ax,4f01h ;\
mov cx,0101h
lea di,buff ;.use VESA mode info call to..
push cs ;.get card stats for mode 101h
pop es
int 10h ;/
add di,4
mov ax,word ptr es:[di] ;get window granularity (in KB)
shl ax,0ah
dec ax
mov cs:granmask,ax ; = granularity - 1 (in Bytes)
not ax
clc
GVL1: inc cs:bitshift ;\
rcl ax,1 ;.just a way to get vars I need :)
jc GVL1 ;/
add cs:bitshift,0fh
inc ax
mov disp64k,ax
add di,8
mov eax,dword ptr es:[di] ;get address of window control
mov cs:winfunc,eax
ret
buff label byte
db 100h dup (?)
endp
Lin proc
;Codesegment: Lin
;Inputs: eax: x1, ebx: y1, cx: x2, dx: y2, bp: color
;Destroys: ax, bx, cx, edx, si, edi
;Global: winfunc(dd),winpos(dw),page(dw),granmask(dw),disp64k(dw),bitshift(db)
;Assumes: eax, ebx have clear high words
cmp dx,bx ;\
ja LinS1 ;.sort vertices
xchg ax,cx
xchg bx,dx ;/
LinS1: sub cx,ax ;\
ja LinS2 ;.calculate deltax and
neg cx ;.modify core loop based on sign
xor cs:xinc1[1],28h ;/
LinS2: sub dx,bx ;deltay
neg dx
dec dx
shl bx,7 ;\
add ax,bx ;.calc linear start address
lea edi,[eax][ebx*4] ;/
mov si,dx ;\
xor bx,bx
mov ax,cs:page ;\
shl ax,2 ;.pageOffset=page*5*disp64K
add ax,cs:page
mul cs:disp64k ;/
push cx ;.initialize CPU window
mov cl,cs:bitshift ;.to top of line
shld edx,edi,cl
pop cx
add dx,ax
and di,cs:granmask
mov cs:winpos,dx
call cs:winfunc
mov dx,si ;/
mov ax,bp
mov bx,dx
;ax:color, bx:err-accumulator, cx:deltaX, dx:vertical count,
;di:location in CPU window, si:deltaY, bp:color
LinL1: mov [di],al ;\
add bx,cx
jns LinS3
LinE1: add di,280h
jc LinR2 ;.core routine to
inc dx ;.render line
jnz LinL1
jmp LinOut
LinL2: mov [di],al ;\
xinc1 label byte
LinS3: add di,1 ;.this deals with
jc LinR1 ;.horizontal pixel runs
LinE2: add bx,si
jns LinL2 ;/
jmp LinE1 ;/
LinR1: js LinS7 ;\
PgDown ;.move page down 64k..
mov ax,bp
jmp LinE2
LinS7: PgUp ;.or up by 'granularity'
mov ax,bp
jmp LinE2 ;/
LinR2: PgDown ;\
mov ax,bp ;.move page down 64k
inc dx
jnz LinL1 ;/
LinOut: mov cs:xinc1[1],0c7h
ret
endp
winfunc dd ? ;fullpointer to VESA setwindow function
winpos dw ? ;temp storage of CPU window position
granmask dw ? ;masks address within window granularity
disp64k dw ? ;number of 'granules' in 64k
page dw 0 ;video page (0,1,2 for 1MB video)
bitshift db 0 ;used to extract high order address bits..
;\ for setting CPU window
ends
stk segment para stack use16 'STACK'
dw 100h dup (?)
ends
end
---CODE END---